#pragma once

#include "Requestor.hpp"
#include "rpc_caller.hpp"
#include "rpc_register.hpp"
#include "rpc_topic.hpp"
#include "../common/dispatcher.hpp"

namespace bitrpc
{
    namespace client
    {
        class RegistryClient
        {
        public:
            using ptr = std::shared_ptr<RegistryClient>;
            // 构造函数传入注册中心的地址信息，用于连接注册中心
            RegistryClient(const string &ip, int port)
                : _requestor(std::make_shared<Requestor>()),
                  _provider(std::make_shared<Provider>(_requestor)),
                  _dispatcher(std::make_shared<dispatcher>())
            {
                auto cb = std::bind(&bitrpc::client::Requestor::onResponse, _requestor.get(),
                                    std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<BaseMessage>(MessageType::RSP_SERVICE, cb);
                auto method = std::bind(&bitrpc::dispatcher::onMessage, _dispatcher.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _client = bitrpc::ClientFactory::create(ip, port);
                _client->setMessageCallback(method);

                _client->connect();
            }
            // 对外提供的服务注册接口
            bool registryMethod(const std::string &method, const Address &host)
            {
                return _provider->registryMethod(_client->connection(), method, host);
            }

        private:
            Requestor::ptr _requestor;
            Provider::ptr _provider;
            dispatcher::ptr _dispatcher;
            BaseClient::ptr _client;
        };

        class DiscoveryClient
        {
        public:
            using ptr = std::shared_ptr<DiscoveryClient>;
            // 构造函数传入注册中心的地址信息，用于连接注册中心
            DiscoveryClient(const string &ip, int port, Discover::OfflinieCallback callback)
                : _requestor(std::make_shared<Requestor>()),
                  _discover(std::make_shared<Discover>(_requestor, callback)),
                  _dispatcher(std::make_shared<dispatcher>())
            {
                auto cb = std::bind(&bitrpc::client::Requestor::onResponse, _requestor.get(),
                                    std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<BaseMessage>(MessageType::RSP_SERVICE, cb);
                // 还要注册上下线通知的回调函数
                auto req_cb = std::bind(&Discover::onServiceRequest, _discover.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<ServiceRequest>(MessageType::REQ_SERVICE, req_cb);

                auto method = std::bind(&bitrpc::dispatcher::onMessage, _dispatcher.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _client = bitrpc::ClientFactory::create(ip, port);
                _client->setMessageCallback(method);

                _client->connect();
            }
            // 对外提供的服务发现接口
            bool serviceDiscover(const std::string &method, Address &host)
            {
                return _discover->serviceDiscover(_client->connection(), method, host);
            }

        private:
            Requestor::ptr _requestor;
            Discover::ptr _discover;
            dispatcher::ptr _dispatcher;
            BaseClient::ptr _client;
        };

        // 短连接思想：服务发现后，获取到服务主机地址，然后实例化客户端对象，连接服务提供者，进行rpc调用，调用完毕后关闭客户端
        // 优点：思想简单，用的时候创建，用完就关闭
        // 缺点：rpc调用效率稍低，rpc调用完毕后关闭连接，在异步处理的时候比较麻烦

        // 采用长连接思想：在客户端有一个连接池
        // 在第一次实例化客户端进行rpc调用后，并不会关闭客户端，而是将客户端放进连接池
        // 在以后再次发出相同rpc调用的时候，发现连接池中有已经建立连接的客户端，就可以直接取出客户端对象，进行rpc调用
        // 难点：一旦收到服务下线通知，就需要从连接池中删除客户端对象
        // 优点：当进行二次rpc调用时，效率高（直接复用之前的客户端对象）

        class RpcClient
        {
        public:
            using ptr = std::shared_ptr<RpcClient>;
            // enableDiscover————是否启用服务发现功能，决定了地址信息时注册中心的地址信息，还是服务提供者的地址信息
            RpcClient(bool enableDiscover, const string &ip, int port)
                : _enableDiscover(enableDiscover),
                  _requestor(std::make_shared<Requestor>()),
                  _caller(std::make_shared<RpcCaller>(_requestor)),
                  _dispatcher(std::make_shared<dispatcher>())
            {
                auto cb = std::bind(&bitrpc::client::Requestor::onResponse, _requestor.get(),
                                    std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<BaseMessage>(MessageType::RSP_RPC, cb);
                // 如果启用了服务发现功能，地址信息是注册中心的地址，是服务发现客户端需要连接的地址，则通过地址信息实例化_discover_client
                // 如果没启用服务发现，地址信息是服务提供者的地址，直接实例化_rpc_client
                if (_enableDiscover == true)
                {
                    auto cb = std::bind(&RpcClient::delClient, this, std::placeholders::_1);
                    _discover_client = std::make_shared<DiscoveryClient>(ip, port, cb);
                }
                else
                {

                    auto method = std::bind(&bitrpc::dispatcher::onMessage, _dispatcher.get(),
                                            std::placeholders::_1, std::placeholders::_2);
                    _rpc_client = ClientFactory::create(ip, port);
                    _rpc_client->setMessageCallback(method);
                    _rpc_client->connect();
                }
            }
            // 对外提供的rpc同步调用接口
            bool call(const std::string &method, const Json::Value &params, Json::Value &result)
            {
                // 服务提供者获取：1.服务发现 2.固定提供者
                BaseClient::ptr client = getClient(method);
                if (client.get() == nullptr)
                {
                    return false;
                }

                // 3.通过客户端连接，发送rpc请求
                return _caller->call(client->connection(), method, params, result);
            }
            // 对外提供的rpc异步调用接口
            bool call(const std::string &method, const Json::Value &params, RpcCaller::JsonAsyncResponse &result)
            {
                // 服务提供者获取：1.服务发现 2.固定提供者
                BaseClient::ptr client = getClient(method);
                if (client.get() == nullptr)
                {
                    return false;
                }

                // 3.通过客户端连接，发送rpc请求
                return _caller->call(client->connection(), method, params, result);
            }
            // 对外提供的rpc回调调用接口
            bool call(const std::string &method, const Json::Value &params, RpcCaller::JsonResponseCallback cb)
            {
                // 服务提供者获取：1.服务发现 2.固定提供者
                BaseClient::ptr client = getClient(method);
                if (client.get() == nullptr)
                {
                    return false;
                }

                // 3.通过客户端连接，发送rpc请求
                return _caller->call(client->connection(), method, params, cb);
            }

        private:
            BaseClient::ptr newClient(const Address &host)
            {
                auto method = std::bind(&bitrpc::dispatcher::onMessage, _dispatcher.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                auto client = ClientFactory::create(host.first, host.second);
                client->setMessageCallback(method);
                client->connect();
                addClient(host, client);
                return client;
            }
            BaseClient::ptr getClient(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);

                if (_rpc_clients.count(host) == 0)
                {
                    return BaseClient::ptr();
                }
                return _rpc_clients[host];
            }
            BaseClient::ptr getClient(const std::string &method)
            {
                // 服务提供者获取：1.服务发现 2.固定提供者
                BaseClient::ptr client;
                if (_enableDiscover)
                {
                    // 1.通过服务发现，获取服务提供者的地址信息
                    Address host;
                    bool ret = _discover_client->serviceDiscover(method, host);
                    if (!ret)
                    {
                        ELOG("当前 %s 服务，没有找到服务提供者！", method.c_str());
                        return BaseClient::ptr();
                    }

                    // 2.查看服务提供者是否已有实例化的客户端，有则直接使用，没有则创建
                    client = getClient(host);
                    if (client.get() == nullptr)
                    {
                        // 没有已实例化的客户端,创建客户端对象
                        client = newClient(host);
                    }
                }
                else
                {
                    client = _rpc_client;
                }
                return client;
            }
            void addClient(const Address &host, const BaseClient::ptr &client)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _rpc_clients[host] = client;
            }
            void delClient(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _rpc_clients.erase(host);
            }

        private:
            struct AddressHashi
            {
                size_t operator()(const Address &host) const
                {
                    std::string addr = host.first + std::to_string(host.second);
                    return std::hash<std::string>{}(addr);
                }
            };
            bool _enableDiscover;
            Requestor::ptr _requestor;

            RpcCaller::ptr _caller;
            DiscoveryClient::ptr _discover_client;
            dispatcher::ptr _dispatcher;
            std::mutex _mutex;
            BaseClient::ptr _rpc_client;                                             // 用于未启用服务发现
            std::unordered_map<Address, BaseClient::ptr, AddressHashi> _rpc_clients; // 用于服务发现的客户端连接池
        };

        class TopicClient
        {
        public:
            TopicClient(const std::string &ip, int port)
                : _requestor(std::make_shared<Requestor>()),
                  _dispatcher(std::make_shared<dispatcher>()),
                  _topic_manager(std::make_shared<TopicManager>(_requestor))
            {
                auto cb = std::bind(&bitrpc::client::Requestor::onResponse, _requestor.get(),
                                    std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<BaseMessage>(MessageType::RSP_TOPIC, cb);
                //将消息发布请求处理回调函数设置进dispatcher
                auto req_cb = std::bind(&TopicManager::onPublish, _topic_manager.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _dispatcher->registerHandler<TopicRequest>(MessageType::REQ_TOPIC, req_cb);

                auto method = std::bind(&bitrpc::dispatcher::onMessage, _dispatcher.get(),
                                        std::placeholders::_1, std::placeholders::_2);
                _rpc_client = ClientFactory::create(ip, port);
                _rpc_client->setMessageCallback(method);
                _rpc_client->connect();
            }
            bool create(const std::string &key)
            {
               return _topic_manager->create(_rpc_client->connection(),key);
            }
            bool remove(const std::string &key)
            {
               return _topic_manager->remove(_rpc_client->connection(),key);

            }
            bool subscribe(const std::string &key, const TopicManager::SubCallback &cb)
            {
               return _topic_manager->subscribe(_rpc_client->connection(),key,cb);

            }
            bool cancel(const std::string &key)
            {
               return _topic_manager->cancel(_rpc_client->connection(),key);

            }
            bool publish(const std::string &key, const std::string &msg)
            {
               return _topic_manager->publish(_rpc_client->connection(),key,msg);
                
            }
            void ShutDown()
            {
                _rpc_client->shutdown();
            }
        private:
            Requestor::ptr _requestor;
            TopicManager::ptr _topic_manager;
            dispatcher::ptr _dispatcher;
            BaseClient::ptr _rpc_client; // 用于未启用服务发现
        };
    }
}
